package com.own.component.log.login.util;

import com.own.base.BaseResponseCode;
import com.own.component.client.base.RequestClientManager;
import com.own.component.common.exception.BusinessSimpleException;
import com.own.component.common.login.user.BaseLoginUser;
import com.own.component.common.login.util.LoginInfoUtil;
import com.own.component.common.util.IpUtil;
import com.own.component.common.util.RequestUtil;
import com.own.component.common.util.executor.ExecutorUtil;
import com.own.component.log.login.core.ExtraLogLoginSpringHook;
import com.own.component.log.login.core.LogLoginHandlerHook;
import com.own.component.log.login.core.PersistenceLogLoginSpringHook;
import com.own.component.log.login.core.impl.CoreDefaultAfterLogLoginHookImpl;
import com.own.component.log.login.em.LogLoginStatusEnum;
import com.own.component.log.login.entity.LogLoginEntity;
import com.own.component.log.login.entity.LoginLastFailInfoEntity;
import com.own.component.util.time.DurationUtil;
import com.own.constant.ConstantCommon;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

/**
 * LogLoginUtil
 *
 * @author chenxueli
 * @date 2023-08-08 15:32:00
 */
@Slf4j
@Component
public class LogLoginUtil {

    @Resource
    private PersistenceLogLoginSpringHook persistenceLogLoginSpringHook;
    @Resource
    private ExtraLogLoginSpringHook extraLogLoginSpringHook;

    @Resource
    private HttpServletRequest request;
    @Resource
    private RequestClientManager requestClientManager;

    @Resource
    private ExecutorUtil executorUtil;
    @Resource
    private LoginInfoUtil loginInfoUtil;

    @Resource
    private CoreDefaultAfterLogLoginHookImpl coreDefaultAfterLogLoginHookImpl;

    @Resource
    private LoginLimitRuleUtil loginLimitRuleUtil;

    @Resource
    private List<LogLoginHandlerHook> logLoginHandlerHookList;

    /**
     * 登录成功日志记录操作
     *
     * @param user 用户信息
     */
    public void success(BaseLoginUser user) {
        success(user, loginInfoUtil.type().get(), loginInfoUtil.account().get(), loginInfoUtil.code().get());
    }

    /**
     * 登录成功日志记录操作
     *
     * @param user 用户信息
     * @param type 登录的方式
     */
    public void success(BaseLoginUser user, String type) {
        success(user, type, null, null);
    }

    /**
     * 登录成功日志记录操作
     *
     * @param loginUser 用户信息
     * @param type      登录的方式
     * @param account   登录的账号
     * @param code      登录的密码
     */
    public void success(BaseLoginUser loginUser, String type, String account, String code) {
        var client = requestClientManager.client(request);
        var entity = new LogLoginEntity()
                .setLoginUser(loginUser)
                .setLoginTime(LocalDateTime.now())
                .setLoginType(type)
                .setLoginAccount(account)
                .setLoginCode(code)
                .setClient(client)
                .setStatus(LogLoginStatusEnum.SUCCESS)
                .setIp(IpUtil.getIpAddress(request))
                .setExtra(extraLogLoginSpringHook.collect(client))
                .setTraceId(MDC.get(ConstantCommon.TRACE_ID))
                .setHeaders(RequestUtil.headerForDecode(request));
        // 添加处理登录日志的操作
        logLoginHandlerHookList.forEach(hook -> hook.run(entity));
        // 持久化操作
        executorUtil.execute(() -> persistenceLogLoginSpringHook.run(entity));
    }

    /**
     * 登录失败日志记录操作
     *
     * @param message 消息
     */
    public void fail(BaseResponseCode message) {
        fail(null, message.desc(), null);
    }

    /**
     * 登录失败日志记录操作
     *
     * @param message 消息
     */
    public void fail(String message) {
        fail(null, message, null);
    }

    /**
     * 登录失败日志记录操作
     *
     * @param code    密码
     * @param message 消息
     */
    public void fail(String code, BaseResponseCode message) {
        fail(code, message.desc(), null);
    }

    /**
     * 登录失败日志记录操作
     *
     * @param code      密码
     * @param message   消息
     * @param loginUser 登录用户信息
     */
    public void fail(String code, BaseResponseCode message, BaseLoginUser loginUser) {
        fail(code, message.desc(), loginUser);
    }

    /**
     * 登录失败日志记录操作
     *
     * @param code      密码
     * @param message   消息
     * @param loginUser 登录用户信息
     */
    public void fail(String code, String message, BaseLoginUser loginUser) {
        var client = requestClientManager.client(request);
        var entity = new LogLoginEntity()
                .setLoginUser(loginUser)
                .setLoginTime(LocalDateTime.now())
                .setLoginType(loginInfoUtil.type().get())
                .setLoginAccount(loginInfoUtil.account().get())
                .setLoginCode(loginInfoUtil.code().get())
                .setStatus(LogLoginStatusEnum.FAIL)
                .setClient(client)
                .setMessage(message)
                .setIp(IpUtil.getIpAddress(request))
                .setExtra(extraLogLoginSpringHook.collect(client))
                .setTraceId(MDC.get(ConstantCommon.TRACE_ID))
                .setHeaders(RequestUtil.headerForDecode(request));
        // 添加处理登录日志的操作
        logLoginHandlerHookList.forEach(hook -> hook.run(entity));
        // 持久化操作
        executorUtil.execute(() -> persistenceLogLoginSpringHook.run(entity));
    }

    /**
     * 验证登录次数
     *
     * @param account 账号
     * @param type    登录的方式
     */
    public void check(String account, String type) {
        var ruleList = loginLimitRuleUtil.get(type);
        if (ruleList == null) {
            return;
        }
        // 获取连续登录失败的次数
        var entity = coreDefaultAfterLogLoginHookImpl.lastLoginTime(account, type);
        if (LoginLastFailInfoEntity.NONE.equals(entity)) {
            return;
        }
        for (var rule : ruleList) {
            if (entity.getCount() >= rule.getMaxFailCount()) {
                // 判断时间
                var time = entity.getLoginTime().plusSeconds(rule.getLockTime());
                var now = LocalDateTime.now();
                if (time.isBefore(now)) {
                    return;
                }
                // 设置可以再次登录的时间
                var message = rule.getMessage()
                        .replace("${count}", entity.getCount() + "")
                        .replace("${max_count}", rule.getMaxFailCount() + "")
                        .replace("${duration}", DurationUtil.format(Duration.between(time, now)));
                // 登录查过指定的限制
                throw new BusinessSimpleException(message);
            }
        }
    }

}
